package com.soundslikeobiwan.ontocalendar;

//import java.lang.*;
import java.util.*;
//import java.util.Date.*;
//import java.io.*;



//import javax.microedition.pim.*;

import javax.microedition.pim.*;
import javax.wireless.messaging.Message;
import javax.wireless.messaging.TextMessage;

//import net.rim.blackberry.api.invoke.CalendarArguments;
import net.rim.blackberry.api.pdap.*;
import net.rim.blackberry.api.invoke.*;
import net.rim.blackberry.api.mail.BodyPart;
import net.rim.blackberry.api.mail.Folder;
import net.rim.blackberry.api.mail.Multipart;
import net.rim.blackberry.api.mail.Part;
import net.rim.blackberry.api.mail.Session;
import net.rim.blackberry.api.mail.Store;
import net.rim.blackberry.api.mail.TextBodyPart;
import net.rim.blackberry.api.mail.Transport;
import net.rim.blackberry.api.menuitem.ApplicationMenuItem;
import net.rim.blackberry.api.menuitem.ApplicationMenuItemRepository;
import net.rim.blackberry.api.options.OptionsManager;
import net.rim.blackberry.api.options.OptionsProvider;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.NumericChoiceField;
import net.rim.device.api.ui.component.ObjectChoiceField;
import net.rim.device.api.ui.component.Status;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.util.Persistable;
import net.rim.blackberry.api.mail.event.FolderEvent;
import net.rim.blackberry.api.mail.event.FolderListener;
import net.rim.blackberry.api.mail.event.MessageEvent;
import net.rim.blackberry.api.mail.event.MessageListener;

/*
 * OntoCalendar application
 * This registers a simple menu item with the system to be shown
 * when viewing an email or SMS Message. The run method for this menuItem
 * simply gets the message for use
 *
 * Copyright (C) 2003 Research In Motion Ltd.
 */
class OntoCalendarOptions implements OptionsProvider {
    // members
    private ObjectChoiceField _getNote;
    
    private NumericChoiceField _duration;
    private NumericChoiceField _reminder;
    
    private OntoCalendarData _data;
    private String _title;
    private static OntoCalendarOptions _instance;
    // constructors
    private OntoCalendarOptions()   {
    }
    private OntoCalendarOptions(String title)       {
        _title = title;
        _data = OntoCalendarData.load();
    }
    // Only allow one instance of this class.
    public static OntoCalendarOptions getInstance() {
        if (_instance == null) {
            _instance = new OntoCalendarOptions("OntoCalendar v3.5.4");
        }
        return _instance;
    }
    // On startup, create the instance and register it.
    public static void Register()       {
        OptionsManager.registerOptionsProvider(getInstance());
    }
    // Get the title for the option item.
    public String getTitle() {
        return _title;
    }
    // Add fields to the screen.
    public void populateMainScreen(MainScreen screen) {
        int curopts = _data.getSelected(); 
        int doCopy = curopts & 1;
        int durationIndex = (curopts & 0x1e) >> 1;    /* increments of 15 minutes */
        int reminderIndex = (curopts & 0x1e0) >> 5;    /* increments of 15 minutes */
        String[] choices = {"No", "Yes"};

        _getNote = new ObjectChoiceField("Copy Message: ", choices, doCopy);
        screen.add(_getNote);
        _duration = new NumericChoiceField ("Default Duration: ", 0, 256, 15, durationIndex);
        screen.add(_duration);
        _reminder = new NumericChoiceField ("Default Reminder: ", 0, 256, 15, reminderIndex);
        screen.add(_reminder);
    } 
    // Save the data.
    public void save()      {
        _data.setSelected(_getNote.getSelectedIndex() + 
                            (_duration.getSelectedIndex() <<1 ) + 
                            (_reminder.getSelectedIndex() << 5));
        _data.commit();
    }
    // Retrieve the data. Used by other applications to access options data.
    public OntoCalendarData getData() {
        return _data;
    }
    // Inner class to store selected option values.
    public static final class OntoCalendarData implements Persistable        {
        private static final long ID = 0x4242477421000001L;
        private int _selectedOption;
        private OntoCalendarData() {
        }
        public int getSelected() {
            return _selectedOption;
        }
        public void setSelected(int index) {
            _selectedOption = index;        
        }
        public void commit() {
            PersistentObject.commit(this);
        }
        private static OntoCalendarData load() {
            PersistentObject persist = PersistentStore.getPersistentObject( OntoCalendarData.ID );
            OntoCalendarData contents = (OntoCalendarData)persist.getContents();
            synchronized( persist ) {
                if( contents == null ) {
                    contents = new OntoCalendarData();
                    persist.setContents( contents );
                    persist.commit();
                }
            }
            return contents;
        }
    }   
}

class OntoCalendar  {
    // A simple tool to put the "current" SMS or Email message into a new calendar item

    public static void main(String[] args){
        System.out.println("Starting OntoCalendar");
        (new EventMenuItem(0)).registerInstance();
        (new TaskMenuItem(0)).registerInstance();

        OntoCalendarOptions.Register();

    }
}

class EventMenuItem extends ApplicationMenuItem
{
    private String _plainTextMessage = "";
    // we'll use the default constructors here.
    EventMenuItem(int order){
        super(order);
    }

    // Register the instance of the menuItem with the system
    public void registerInstance(){
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
        ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
        ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
        ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);

    }

    //methods we must implement

    //toString should return the string we want to
    //use as the lable of the menuItem
    public String toString(){
        return "Onto Calendar...";
    }

    //Run is called when the menuItem is invokes
    public Object run(Object context) {
                        
        EventList el;
        ToDoList tl;
        
        javax.microedition.pim.Event newEvent=null;
        
        int curopts = OntoCalendarOptions.getInstance().getData().getSelected();
        int doCopy = curopts & 1;
        int durationIndex = (curopts & 0x1e) >> 1;    /* increments of 15 minutes */
        int reminderIndex = (curopts & 0x1e0) >> 5;    /* increments of 15 minutes */
        int alarm = 60 * 15 * reminderIndex;

        
        int period = (durationIndex * 15)* 60*1000;
        // start event on the next period
        long now=System.currentTimeMillis();
        long ceil;
        if (period == 0) {
            ceil = now;
        } else {
            ceil = ( now + period - 1 ) / period * period;
        }
 
        try {
            String msgContent;
            
            int attr = Event.ATTR_NONE;
            int body = Event.NOTE;
            int subject = Event.SUMMARY;
            
            el = (EventList)PIM.getInstance(). openPIMList(PIM.EVENT_LIST, PIM.READ_WRITE);
            newEvent = el.createEvent();
            newEvent.addDate(Event.START, Event.ATTR_NONE, ceil);
            newEvent.addDate(Event.END, Event.ATTR_NONE, period+ceil);
            if (el.isSupportedField(Event.ALARM) && reminderIndex > 0) {
                newEvent.addInt(Event.ALARM, Event.ATTR_NONE, alarm); 
            }


            if (context instanceof TextMessage) {
                TextMessage message = (TextMessage) context;
                
                msgContent = message.getPayloadText();
                if (null != msgContent) {
                        newEvent.addString(body, attr, msgContent);
                }
            } else if (context instanceof net.rim.blackberry.api.mail.Message) {
                net.rim.blackberry.api.mail.Message message = (net.rim.blackberry.api.mail.Message) context;
               
                msgContent = message.getSubject();
                if (null != msgContent) {
                    newEvent.addString(subject, attr, msgContent);
                }               

                if (doCopy > 0) {
                    findEmailBody(message.getContent());
                    newEvent.addString(body, attr, _plainTextMessage);
                }
            }

        } catch (Exception ex) {
            Dialog.alert(ex.getClass().getName() + ": " + ex.getMessage());
            return null;
        }
        
        Invoke.invokeApplication(Invoke.APP_TYPE_CALENDAR, new CalendarArguments(CalendarArguments.ARG_NEW, newEvent) );
        return context;
    }

    //This method reads within an email message to find the email body.
    private void findEmailBody(Object obj)
    {
        
        if(obj instanceof Multipart)
        {
            Multipart mp = (Multipart)obj;
            
            //Extract all of the parts within the Multipart message.
            for(int count=0; count < mp.getCount(); ++count)
            {
                findEmailBody(mp.getBodyPart(count));
            }
        }
        else if (obj instanceof TextBodyPart)
        {
            //This message only has a text body.
            TextBodyPart tbp = (TextBodyPart)obj;
            readEmailBody(tbp);
        } else if (obj instanceof BodyPart) {
            BodyPart bp = (BodyPart)obj;

            if (bp.getContentType().indexOf("text/html") != -1)
            {
                //The message has no attachments.
                //Read the email body, which may contain a TexBodyPart, MimeBodyPart or both.
                readEmailBody(bp);
            }
            else

               if (bp.getContentType().equals("multipart/mixed") || 
                    bp.getContentType().equals("multipart/alternative"))
            {
                //The message has attachments or we are at the top level of the message.  Dig deeper to find the body.
                //Extract all of the parts within the MimeBodyPart message.
                findEmailBody(bp.getContent());
            }   
        }           
    }
        
        
    private void readEmailBody(TextBodyPart tbp)
    {
        //This is the plain text body.
        _plainTextMessage = (String)tbp.getContent();
        
        //Determine if all of the text body part is present.
        if (tbp.hasMore() && !tbp.moreRequestSent())
        {
            //It does, request more of the message.
            try
            {
                Transport.more((BodyPart)tbp, true);
                Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
            }
            catch (Exception ex)
            {
                Dialog.alert("Exception: " + ex.toString());
            }
        }        
    }
   
    //Displays the HTML or plain text body of the email message.
    private void readEmailBody(BodyPart bp)
    {
        //Extract the content of the message.
        Object obj = bp.getContent();
        String mimeType = bp.getContentType();
        String body = null;
        
        //Determine if the data returned is a String or a byte array.
        //If the BlackBerry is able to convert the HTML content into a String,
        //then a String should be returned.  If the encoding is not supported a 
        //byte array is returned to allow your application to work with the raw data.
        if (obj instanceof String)
        {
            body = (String)body;
        }
        else if (obj instanceof byte[])
        {
            body = new String((byte[])obj);
        }
        
        if (mimeType.indexOf(BodyPart.ContentType.TYPE_TEXT_PLAIN_STRING) != -1)
        {
            //This is the plain text body.
            _plainTextMessage = body;
            
            //Determine if all of the text body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                }
            }                
        }
        else if (mimeType.indexOf("text/html") != -1)
        {
            //Determine if all of the HTML body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the HTML message body.  Reopen the screen to view it once more has been received.");
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                }
            }                
        }
    }

}

class TaskMenuItem extends ApplicationMenuItem
{
    private String _plainTextMessage = "";
    // we'll use the default constructors here.
    TaskMenuItem(int order){
        super(order);
    }

    // Register the instance of the menuItem with the system
    public void registerInstance(){
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
        ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_EMAIL_VIEW,this);
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
        ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_SMS_VIEW,this);
        ApplicationMenuItemRepository.getInstance().removeMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);
        ApplicationMenuItemRepository.getInstance().addMenuItem(ApplicationMenuItemRepository.MENUITEM_MESSAGE_LIST,this);

    }

    //methods we must implement

    //toString should return the string we want to
    //use as the lable of the menuItem
    public String toString(){
        return "Onto Tasks...";
    }
    
    private void readSupportedFields() {
        System.out.println("TodoList contents:");        
        try {
            String[] lists = PIM.getInstance().listPIMLists(PIM.TODO_LIST);
            int length = lists.length;
            ToDoList[] todoList = new ToDoList[length];
            for (int i = 0; i < length; i++) {
            
                todoList[i] = (ToDoList) PIM.getInstance().openPIMList(PIM.TODO_LIST, PIM.READ_WRITE, lists[i]);
                System.out.println(i+1+": "+lists[i]);
                int[] fields = todoList[i].getSupportedFields();
                System.out.println("Supported fields:");
                for (int j = 0; j < fields.length; j++) {
                        System.out.println(fields[j]+"="+todoList[i].getFieldLabel(fields[j]));
                }
            }           
        } catch (PIMException pe) {
        // no such list
        } catch (SecurityException se) {
            // MIDlet is not allowed to access to the specified list
        }
    }
    private void readFirstTask() {
        System.out.println("ToDo content:");        
        try {
            String[] lists = PIM.getInstance().listPIMLists(PIM.TODO_LIST);
            int length = lists.length;
            ToDoList[] todoList = new ToDoList[length];
            for (int i = 0; i < length; i++) {
            
                todoList[i] = (ToDoList) PIM.getInstance().openPIMList(PIM.TODO_LIST, PIM.READ_WRITE, lists[i]);
                System.out.println(i+1+": "+lists[i]);

                System.out.println("Supported fields:");
                for (Enumeration e = todoList[i].items() ; e.hasMoreElements() ;) 
                {
                    BlackBerryToDo todo = (BlackBerryToDo) e.nextElement();
                    int[] fields = todo.getFields();
                    for (int j = 0; j < fields.length; j++) 
                    {
                        System.out.print(fields[j]+"="+todoList[i].getFieldLabel(fields[j])+"/"+todoList[i].getAttributes());
                        if (fields[j] == BlackBerryToDo.REMINDER) {
                            System.out.println(todoList[i].getDate(BlackBerryToDo.REMINDER, 0));
                        }
                    }
                }      
            }
     
        } catch (PIMException pe) {
        // no such list
        } catch (SecurityException se) {
            // MIDlet is not allowed to access to the specified list
        }

    }
    
    //Run is called when the menuItem is invokes
    public Object run(Object context) {
                        
        EventList el;
        ToDoList tl;
        
        BlackBerryToDo newTask = null;
        
        int curopts = OntoCalendarOptions.getInstance().getData().getSelected();
        int doCopy = curopts & 1;
        int durationIndex = (curopts & 0x1e) >> 1;    /* increments of 15 minutes */
        int reminderIndex = (curopts & 0x1e0) >> 5;    /* increments of 15 minutes */
        int alarm = 60 * 15 * reminderIndex;

        
        int period = (durationIndex * 15)* 60*1000;
        // start event on the next period
        long now=System.currentTimeMillis();
        long ceil;
        if (period == 0) {
            ceil = now;
        } else {
            ceil = ( now + period - 1 ) / period * period;
        }
 
        try {
            String msgContent;
            
            int attr =  ToDo.ATTR_NONE;
            int body = ToDo.NOTE;
            int subject = ToDo.SUMMARY;
                            
            tl = (ToDoList)PIM.getInstance().openPIMList(PIM.TODO_LIST, PIM.READ_WRITE);
            newTask = (BlackBerryToDo)tl.createToDo();
            newTask.addDate(ToDo.DUE, ToDo.ATTR_NONE, ceil);
            
            readSupportedFields();
            readFirstTask();
            
            if (tl.isSupportedField(BlackBerryToDo.REMINDER) && reminderIndex > 0) {
                newTask.addDate(BlackBerryToDo.REMINDER, BlackBerryToDo.ATTR_NONE, ceil - alarm*1000);
            }
            if (tl.isSupportedField(Event.ALARM) && reminderIndex > 0) {
                newTask.addDate(Event.ALARM, BlackBerryToDo.ATTR_NONE, alarm);
            }

            if (doCopy > 0) {
                if (context instanceof TextMessage) {
                    TextMessage message = (TextMessage) context;
                    
                    msgContent = message.getPayloadText();
                    if (null != msgContent) {
                            newTask.addString(body, attr, msgContent);
                    }
                } else {
                    if (context instanceof net.rim.blackberry.api.mail.Message) {
                        net.rim.blackberry.api.mail.Message message = (net.rim.blackberry.api.mail.Message) context;
                    
                        msgContent = message.getSubject();
                        if (null != msgContent) {
                            newTask.addString(subject, attr, msgContent);
                        }               
                        findEmailBody(message.getContent());
                        newTask.addString(body, attr, _plainTextMessage);
                    }
                }
            }
        } catch (Exception ex) {
            Dialog.alert(ex.getClass().getName() + ": " + ex.getMessage());
            return null;
        }
        
        Invoke.invokeApplication(Invoke.APP_TYPE_TASKS, new TaskArguments(TaskArguments.ARG_NEW, newTask) );
        return context;
    }

    //This method reads within an email message to find the email body.
    private void findEmailBody(Object obj)
    {
        
        if(obj instanceof Multipart)
        {
            Multipart mp = (Multipart)obj;
            
            //Extract all of the parts within the Multipart message.
            for(int count=0; count < mp.getCount(); ++count)
            {
                findEmailBody(mp.getBodyPart(count));
            }
        }
        else if (obj instanceof TextBodyPart)
        {
            //This message only has a text body.
            TextBodyPart tbp = (TextBodyPart)obj;
            readEmailBody(tbp);
        } else if (obj instanceof BodyPart) {
            BodyPart bp = (BodyPart)obj;

            if (bp.getContentType().indexOf("text/html") != -1)
            {
                //The message has no attachments.
                //Read the email body, which may contain a TexBodyPart, MimeBodyPart or both.
                readEmailBody(bp);
            }
            else

               if (bp.getContentType().equals("multipart/mixed") || 
                    bp.getContentType().equals("multipart/alternative"))
            {
                //The message has attachments or we are at the top level of the message.  Dig deeper to find the body.
                //Extract all of the parts within the MimeBodyPart message.
                findEmailBody(bp.getContent());
            }   
        }           
    }
        
        
    private void readEmailBody(TextBodyPart tbp)
    {
        //This is the plain text body.
        _plainTextMessage = (String)tbp.getContent();
        
        //Determine if all of the text body part is present.
        if (tbp.hasMore() && !tbp.moreRequestSent())
        {
            //It does, request more of the message.
            try
            {
                Transport.more((BodyPart)tbp, true);
                Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
            }
            catch (Exception ex)
            {
                Dialog.alert("Exception: " + ex.toString());
            }
        }        
    }
   
    //Displays the HTML or plain text body of the email message.
    private void readEmailBody(BodyPart bp)
    {
        //Extract the content of the message.
        Object obj = bp.getContent();
        String mimeType = bp.getContentType();
        String body = null;
        
        //Determine if the data returned is a String or a byte array.
        //If the BlackBerry is able to convert the HTML content into a String,
        //then a String should be returned.  If the encoding is not supported a 
        //byte array is returned to allow your application to work with the raw data.
        if (obj instanceof String)
        {
            body = (String)body;
        }
        else if (obj instanceof byte[])
        {
            body = new String((byte[])obj);
        }
        
        if (mimeType.indexOf(BodyPart.ContentType.TYPE_TEXT_PLAIN_STRING) != -1)
        {
            //This is the plain text body.
            _plainTextMessage = body;
            
            //Determine if all of the text body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the plain text message body.  Reopen the screen to view it once more has been received.");
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                }
            }                
        }
        else if (mimeType.indexOf("text/html") != -1)
        {
            //Determine if all of the HTML body part is present.
            if (bp.hasMore() && !bp.moreRequestSent())
            {
                //It does, request more of the message.
                try
                {
                    Transport.more((BodyPart)bp, true);
                    Status.show("Requesting more of the HTML message body.  Reopen the screen to view it once more has been received.");
                }
                catch (Exception ex)
                {
                    Dialog.alert("Exception: " + ex.toString());
                }
            }                
        }
    }


}

/*
// based on http://tinyurl.com/5a82jf

class Listener implements FolderListener,MessageListener {

    private Store store;

    public Listener() {
        
        try {
            Session session = Session.waitForDefaultSession();
            store = session.getStore();
            store.addFolderListener(this);
        } catch (Exception e) {
            // TODO: Errorhandling
        }
    }

    public void destroyInboxListener() {
        if (store != null) {
            store.removeFolderListener(this);
        }
    }

    private void processEvent(Message message) {
        Object content = ((Part) message).getContent();
        if (content instanceof BodyPart) {
            BodyPart bodyPart = (BodyPart) content;
            if (bodyPart.hasMore()) {
                // message has more parts, wait for them

                if (!bodyPart.moreRequestSent()) {
                    // no request for other parts yet

                    // add listener to this message
                    ((net.rim.blackberry.api.mail.Message) message).addMessageListener((MessageListener) this);
                    
                    // request other parts
                    try {
                        Transport.more(bodyPart, true);
                    } catch (Exception e) {
                    }
                }
            } else {
                // message complete, remove listener
                ((net.rim.blackberry.api.mail.Message) message).removeMessageListener(this);
                
                String completeContent = ((net.rim.blackberry.api.mail.Message) message).getBodyText();

                //process content here

                //delete the message
                try {
                    Folder folder = ((net.rim.blackberry.api.mail.Message) message).getFolder();
                    folder.deleteMessage((net.rim.blackberry.api.mail.Message) message);
                } catch (Exception e) {
                    // TODO: Errorhandling
                }
            }
        }
    }

        public void messagesAdded(FolderEvent e) {
                // TODO Auto-generated method stub
                
        }

        public void messagesRemoved(FolderEvent e) {
                // TODO Auto-generated method stub
                
        }

        public void changed(MessageEvent e) {
                // TODO Auto-generated method stub
                
        }


} 
*/
